import numpy as np
import S_Draw
import C_Load_Process_Map

# 基础方法(根据不同的优化目标进行解码)
# 解码部分 将编码转为工序图，返回最大完成时间
# 根据OS和MS计算：每个工序的详细信息  方法适应度计算  目标值
# def My_Decoder(Idv, DataStructure):
def My_Decoder():
    # 个体
    PS_Chromosome = ['PC_0_01_0', 'PC_0_02_0', 'PC_0_03_0', 'PC_0_02_1']
    LS_Chromosome = ['1#', '2#', '1#', '2#']
    Map = C_Load_Process_Map.Load_Process_Map_Me()
    # PS_Chromosome = ['PC_0_01_0', 'PC_0_01_1', 'PC_0_01_2', 'PC_0_01_3', 'PC_0_02_1', 'PC_0_02_2', 'PC_0_02_3', 'PC_0_02_4', 'PC_0_02_5',  'PC_0_03_1', 'PC_0_03_2', 'PC_0_03_3', 'PC_0_03_4']
    # LS_Chromosome = ['1#', '2#', '1#', '2#','1#', '2#', '1#', '2#','1#', '2#', '1#', '2#', '2#']

    # 构造需要的数据集
    PC_Line = {}  # 构件-生产线对。1：对应关系，2：生产顺序（同一生产线工位占用对构件工序使用的影响），3：解码依据
    for line in Map.Line_Type_Opens[0]:
        PC_Line[line] = []  # 1:从0开始标记，2：采用append添加元素，添加构件编号

    # <<<< 采用深拷贝函数返回，每次都是新的数据结构 >>>>
    PC_DS = Map.Get_PC_DS()
    Resource_DS = Map.Get_Resource_DS()

    # PC_DS 预处理 （Resource_DS 不需要）
    for i in range(len(PS_Chromosome)):
        PC_Code = PS_Chromosome[i]
        Line_Code = LS_Chromosome[i]
        List_Times = Map.Component_Types_Details[Map.Order_Details[PC_Code]]['Process_Time']  # 存储构件的编号与型号的对应关系
        Line_Index = Map.Line_Type_Opens[1][Map.Line_Type_Opens[0].index(Line_Code)]  # 存储产线的编号与索引的对应关系
        for time in List_Times:
            PC_DS[PC_Code][2].append(time[Line_Index])

        #  型号：{
        #       0 工序持续时间：【整型】 特别注意：需要确定产线产能确定时间
        #       1 工序开始时间：【整型】
        #       2 工序结束时间：【整型】
        #       3 所需模具编号：【字符串】
        #       4 所需工人编号：【字符串】
        # }
    # 以遍历的构件，存入 PC_Line，通过PC_Line[Li_Code][0]获取该生产线上紧前构件中最后一个生产的构件编号
    # 通过PC_DS迭代获取各个工序的结束时间，即各工序的结束时间（工序默认与工位等价）
    for i in range(len(PS_Chromosome)):  # 构件下标
        Li_Code = LS_Chromosome[i]
        PC_Code = PS_Chromosome[i]
        Pre_PC_Code = 'null' if len(PC_Line[Li_Code]) == 0 else PC_Line[Li_Code][len(PC_Line[Li_Code])-1]  # null 或者构件编号
        for j in range(len(PC_DS[PC_Code][2])):  # j是工序下标
            # 基准时间
            CurPC_Process_End_Time = 0 if j == 0 else PC_DS[PC_Code][1][j - 1]                 # CurPC：当前构件的紧前工序
            PrePC_Process_End_Time = 0 if Pre_PC_Code == 'null' else PC_DS[Pre_PC_Code][2][j]  # PrePC：紧前构件的当前工序
            Base_Time = max(CurPC_Process_End_Time, PrePC_Process_End_Time)  # 基准：构件工序非资源约束开始时间
            # realse_max_times = [Base_Time]  # 基于基准时间，存放各资源的最大释放时间
            realse_max_times = [[] for _ in range(2)]
            if(PC_Code == 'PC_0_03_0'):
                print('Base_Time:', Base_Time)


            # 资源释放时间(关键理解：资源需求为null的，资源不添加到tiem_Map；更新资源时间时，也不用考虑资源为空的情况)
            for r in range(Map.Resource_Total):
                Resource_Type = PC_DS[PC_Code][r + 3][j]
                if Resource_Type != 'null':  # 资源类型是否为空，否则不添加
                    Resource_DS[Resource_Type].sort()  # 该资源排序，方便后面寻找需要更新资源的下标（小到大）
                    Time, index = Get_Min_Time_of_Base_Time(Resource_DS, Resource_Type, Base_Time)
                    realse_max_times[0].append(Time)
                    realse_max_times[1].append(index)
                else:
                    realse_max_times[0].append(0)
                    realse_max_times[1].append(0)
            # for r in range(Map.Resource_Total):
            #     Resource_Type = PC_DS[PC_Code][r + 3][j]
            #     if Resource_Type != 'null':  # 资源类型是否为空，否则不添加
            #         Resource_DS[Resource_Type].sort()  # 该资源排序，方便后面寻找需要更新资源的下标（小到大）
            #         Time_R = Get_Min_Time_of_Base_Time(Resource_DS, Resource_Type, Base_Time)
            #         realse_max_times.append(Time_R)
            if (PC_Code == 'PC_0_03_0'):
                print('W1资源', Resource_DS['Worker01'])

            # 寻找构件工序最早的开始时间
            resource_max_time = max(realse_max_times[0])
            realse_max_time = max(resource_max_time, Base_Time)
            if(PC_Code == 'PC_0_03_0'):
                print('realse_max_times[0]:', realse_max_times[0])
            if (PC_Code == 'PC_0_03_0'):
                print('--------------------------')
            # if(PC_Code == 'PC_0_03_0'):
            #     print('\t','realse_max_time: ',realse_max_time)
            # 更新工序约束时间 （1）当前构件工序的开始、结束时间
            PC_DS[PC_Code][0].append(realse_max_time)
            PC_DS[PC_Code][1].append(realse_max_time + PC_DS[PC_Code][2][j])

            # 更新资源释放地图 （2）资源根据下标更改释放时间
            for r in range(Map.Resource_Total):
                Resource_Type = PC_DS[PC_Code][r + 3][j]
                if Resource_Type != 'null':  # 资源类型是否为空，否则不添加
                    Resource_DS[Resource_Type][realse_max_times[1][r]] = PC_DS[PC_Code][1][j]
            # for r in range(Map.Resource_Total):
            #     Resource_Type = PC_DS[PC_Code][r + 3][j]
            #     if Resource_Type != 'null':  # 资源类型是否为空，否则不添加
            #         resource_time_index = Get_Max_Time_of_realse_max_time(Resource_DS, Resource_Type, realse_max_time)
            #         Resource_DS[Resource_Type][resource_time_index] = PC_DS[PC_Code][1][j]
        # 更新产线-构件的对应关系
        PC_Line[Li_Code].append(PC_Code)
    print('PC_Line')
    print("\t1# ", PC_Line['1#'])
    print("\t2# ", PC_Line['2#'])
    print('详细的生产工序图')
    for pc_code in PS_Chromosome:
        print("\t", pc_code, '开始时间', PC_DS[pc_code][0])
        print("\t", pc_code, '结束时间', PC_DS[pc_code][1])
        print("\t", pc_code, '持续时间', PC_DS[pc_code][2])
        print()
    return PC_DS


def Get_Min_Time_of_Base_Time(Resource_DS, Resource_Type, Base_Time):  # 以基准时间，获取 >=Base_Time 的最小值
    # 1. 一个循环（1）寻找>=Base_Time的值，返回该值（2）如果没有，返回最大值及其下标
    time_array = Resource_DS[Resource_Type]
    # if (Base_Time >= time_array[len(time_array) - 1]):
    #     return time_array[len(time_array) - 1]
    # else:
    #     for i in range(len(time_array)):
    #         if (time_array[i] >= Base_Time):
    #             return time_array[i]
    if(Base_Time >= time_array[len(time_array) - 1]):
        return time_array[len(time_array) - 1], len(time_array) - 1
    else:
        if(Base_Time <= time_array[0]):
            return time_array[0], 0
        else:
            for i in range(len(time_array)-2, -1, -1):
                if(time_array[i] <= Base_Time):
                    return time_array[i], i
def Get_Max_Time_of_realse_max_time(Resource_DS, Resource_Type, realse_max_time):  # 以最大释放时间为基准，获取 <=realse_max_time 的最大值
    # 2. 一个循环（1）寻找>=Base_Time的值，返回值和下标（2）如果没有，返回最大值及其下标
    time_array = Resource_DS[Resource_Type]
    for i in range(len(time_array)-1, -1, -1):
        if (time_array[i] <= realse_max_time):
            return i


if __name__ == "__main__":
    PC_DS = My_Decoder()
    PS_Chromosome = ['PC_0_01_0', 'PC_0_02_0', 'PC_0_03_0', 'PC_0_02_1']
    LS_Chromosome = ['1#', '2#', '1#', '2#']
    S_Draw.Draw_Gatt_New(PS_Chromosome,LS_Chromosome,PC_DS)
    # a = [2,4,6,7,8,9]
    # print(max(a,100))
    # for i in range(len(a)-2,0,-1):
    #     print(i ,end=',')
    #     # if (a[i] <= 5):
    #     #     print('索引')
    #     #     print(i)
    #     #     break


# 三目标
#       1.构件最短完工时间 makespan
#       2.关键生产线负载 生产线负载最大的
#       3.生产线总负载 生产线全部的加工时间
def Adaptability_For_MLoad_MCLoad_MY(Idv, DataStructure):
    PC_DataStructure = My_Decoder(Idv, DataStructure)

    # PS_Chromosome = Idv.PS_Chromosome
    # LS_Chromosome = Idv.LS_Chromosome
    # Line_Type_Opens = DataStructure.Line_Type_Opens
    # # 遍历全部生产线，获取生产线总负载，，过程中得到makespan和关键生产线负载
    # Target_Min_Time = 0
    # Target_Line_All_Time = 0
    # Target_Line_Key_Time = 0
    # Target = {}
    # for Line_Code_Open in Line_Type_Opens:
    #         Target[Line_Code_Open] = {}
    #         Target[Line_Code_Open]['Sum_Time'] = 0
    #         Target[Line_Code_Open]['End_Time'] = 0
    # for Index in range(len(PS_Chromosome)):
    #         PL_Code = LS_Chromosome[Index]
    #         PC_Code = PS_Chromosome[Index]
    #         PC_Start_Time = PC_DataStructure[PC_Code][7]
    #         PC_End_Time = PC_DataStructure[PC_Code][8]
    #         PC_Procee_Len = len(PC_DataStructure[PC_Code][0])
    #         for Process_Index in range(PC_Procee_Len):
    #                 Target[PL_Code]['Sum_Time'] += (PC_End_Time[Process_Index]-PC_Start_Time[Process_Index])
    #         Target[PL_Code]['End_Time'] = PC_End_Time[PC_Procee_Len-1]
    # a = [] # 最后完工时间
    # b = [] # 各个生产线的负载
    # for Line_Code in Target:
    #         a.append(Target[Line_Code]['End_Time'])
    #         b.append(Target[Line_Code]['Sum_Time'])
    # Target_Min_Time = max(a)
    # Target_Line_Key_Time = max(b)
    # for i in range(len(b)):
    #         Target_Line_All_Time += b[i]

    return 0


########################################################################################################################
########################################################################################################################
# 基础方法(根据不同的优化目标进行解码)
# 解码部分 将编码转为工序图，返回最大完成时间
# 根据OS和MS计算：每个工序的详细信息  方法适应度计算  目标值
def Decoder(Idv, Map):  # 个体  数据集的基本信息
    Machine_Set = Idv.Machine_Chromosome  # 机器编码
    Job_Set = Idv.Job_Chromosome  # 编码
    Job_List_Time = []  # 记录每道工序在特定机器上所需时间
    Job_List_Order = np.array(np.zeros(Map.Job_Num, dtype=int))  # 为每道工件确定工序
    Job_List_Last_Time = np.array(np.zeros(Map.Job_Num, dtype=int))  # 记录前工序的结束时间与机器开工时间取最大
    Start_Time = np.array(np.zeros((Map.Machine_Num, Map.Chromosome_Length), dtype=int))  # 每个工序的开始时间
    End_Time = np.array(np.zeros((Map.Machine_Num, Map.Chromosome_Length), dtype=int))

    Temp_M = 0
    for i in range(Map.Job_Num):  # 设置工序在机器上所用时间（job --> job_list --> job_list_time 工件 工序 工序生产线时间 ）
        Job = Map.Machine_Process_Map[i]  # 所有工序的一个集合
        for j in range(len(Job)):
            Job_List = Job[j]  # 工件每个工序对应所有生产线的生产时间（每个工序对应机器的生产时间）
            Job_List_Time.append(Job_List[Idv.Machine_Chromosome[Temp_M] - 1])
            Temp_M = Temp_M + 1
    Job_List_Time = np.array(Job_List_Time, dtype=int)  # 自带排序属性

    for i in range(Map.Chromosome_Length):  # 遍历工件
        Pre_Job_List_Time = 0  # 工序的前工序完成时间
        Machine_Release_Time = 0  # 机器释放时间
        Pre_All_List = 0  # 前所有工件的工序的数量总和，计算机器编码的下标
        for m in range(Map.Job_Num):  # m工件下标（0-2）（3工件，18工序）
            if m == Job_Set[i] - 1:  # Job_Set[i] - 1 ： 表示工件的下标，也是对应机器的下标
                break
            Pre_All_List += len(Map.Machine_Process_Map[m])
        Machine_Index = int(Pre_All_List + Job_List_Order[Job_Set[i] - 1])  # 加工机器的下标
        # Machine_Num = Machine_Set[Machine_Index]  # 工序加工的机器号
        Machine_Num = Machine_Set[i]  # 工序加工的机器号
        End_Time[Machine_Num - 1][i] = Job_List_Time[Machine_Index]  # 先写工序结束时间
        # 再确定开始时间，比较前工序完成时间和机器释放时间，取其中大者
        if (i == 0):  # 机器释放时间
            Machine_Release_Time = 0  # 如果是第一个工序
        else:
            j = i - 1
            while (j != -1 and End_Time[Machine_Num - 1][j] == 0):  # 找到机器释放时间
                j = j - 1
            if j == -1:  # 前面全0 此机器尚未被使用
                Machine_Release_Time = 0
            else:
                Machine_Release_Time = End_Time[Machine_Num - 1][j]
        Pre_Job_List_Time = Job_List_Last_Time[Job_Set[i] - 1]  # 前工序结束时间
        if Pre_Job_List_Time >= Machine_Release_Time:
            Start_Time[Machine_Num - 1][i] = Pre_Job_List_Time
        else:
            Start_Time[Machine_Num - 1][i] = Machine_Release_Time
        # Start_Time[Machine_Num - 1][i]  = Machine_Release_Time
        End_Time[Machine_Num - 1][i] = End_Time[Machine_Num - 1][i] + Start_Time[Machine_Num - 1][i]
        Job_List_Order[Job_Set[i] - 1] += 1  # 记录下一工序
        Job_List_Last_Time[Job_Set[i] - 1] = End_Time[Machine_Num - 1][i]  # 记录工件已完成工序的时间
    # 返回所有工序的开始时间，结束时间，所需加工时间，和每个工件完成时间
    print("模块：SetAdp\t方法：Decoder(Idv,Map):")
    print("Machine_Set   ", Machine_Set)
    print("Job_Set       ", Job_Set)
    print("Job_List_Time ", Job_List_Time)
    print("Job_List_Order", Job_List_Order)
    print("Start_Time\n", Start_Time)
    print("End_Time\n", End_Time)
    print("Job_List_Last_Time", Job_List_Last_Time)
    return Start_Time, End_Time, Job_List_Time, Job_List_Last_Time


def Decoder_Mould(Idv, Map):  # 个体  数据集的基本信息
    Machine_Set = Idv.Machine_Chromosome  # 机器编码
    Job_Set = Idv.Job_Chromosome  # 编码
    Job_List_Time = []  # 记录每道工序在特定机器上所需时间
    Job_List_Order = np.array(np.zeros(Map.Job_Num, dtype=int))  # 为每道工件确定工序(哨兵表示)
    Job_List_Last_Time = np.array(np.zeros(Map.Job_Num, dtype=int))  # 记录前工序的结束时间与机器开工时间取最大
    Start_Time = np.array(np.zeros((Map.Machine_Num, Map.Chromosome_Length), dtype=int))  # 每个工序的开始时间
    End_Time = np.array(np.zeros((Map.Machine_Num, Map.Chromosome_Length), dtype=int))

    Temp_M = 0
    for i in range(Map.Job_Num):  # 设置工序在机器上所用时间（job --> job_list --> job_list_time 工件 工序 工序生产线时间 ）
        Job = Map.Machine_Process_Map[i]  # 所有工序的一个集合
        for j in range(len(Job)):
            Job_List = Job[j]  # 工件每个工序对应所有生产线的生产时间（每个工序对应机器的生产时间）
            Job_List_Time.append(Job_List[Idv.Machine_Chromosome[Temp_M] - 1])
            Temp_M = Temp_M + 1
    Job_List_Time = np.array(Job_List_Time, dtype=int)  # 自带排序属性

    for i in range(Map.Chromosome_Length):  # 遍历工件
        Pre_Job_List_Time = 0  # 工序的前工序完成时间
        Machine_Release_Time = 0  # 机器释放时间
        Pre_All_List = 0  # 前所有工件的工序的数量总和，计算机器编码的下标
        for m in range(Map.Job_Num):  # m工件下标（0-2）（3工件，18工序）
            if m == Job_Set[i] - 1:  # Job_Set[i] - 1 ： 表示工件的下标，也是对应机器的下标
                break
            Pre_All_List += len(Map.Machine_Process_Map[m])
        Machine_Index = int(Pre_All_List + Job_List_Order[Job_Set[i] - 1])  # 加工机器的下标
        # Machine_Num = Machine_Set[Machine_Index]  # 工序加工的机器号
        Machine_Num = Machine_Set[i]  # 工序加工的机器号
        End_Time[Machine_Num - 1][i] = Job_List_Time[Machine_Index]  # 先写工序结束时间
        # 再确定开始时间，比较前工序完成时间和机器释放时间，取其中大者
        if (i == 0):  # 机器释放时间
            Machine_Release_Time = 0  # 如果是第一个工序
        else:
            j = i - 1
            while (j != -1 and End_Time[Machine_Num - 1][j] == 0):  # 找到机器释放时间
                j = j - 1
            if j == -1:  # 前面全0 此机器尚未被使用
                Machine_Release_Time = 0
            else:
                Machine_Release_Time = End_Time[Machine_Num - 1][j]
        Pre_Job_List_Time = Job_List_Last_Time[Job_Set[i] - 1]  # 前工序结束时间
        if Pre_Job_List_Time >= Machine_Release_Time:
            Start_Time[Machine_Num - 1][i] = Pre_Job_List_Time
        else:
            Start_Time[Machine_Num - 1][i] = Machine_Release_Time
        End_Time[Machine_Num - 1][i] = End_Time[Machine_Num - 1][i] + Start_Time[Machine_Num - 1][i]
        Job_List_Order[Job_Set[i] - 1] += 1  # 记录下一工序
        Job_List_Last_Time[Job_Set[i] - 1] = End_Time[Machine_Num - 1][i]  # 记录工件已完成工序的时间
    # 返回所有工序的开始时间，结束时间，所需加工时间，和每个工件完成时间
    print("模块：SetAdp\t方法：Decoder(Idv,Map):")
    print("Machine_Set   ", Machine_Set)
    print("Job_Set       ", Job_Set)
    print("Job_List_Time ", Job_List_Time)
    print("Job_List_Order", Job_List_Order)
    print("Start_Time\n", Start_Time)
    print("End_Time\n", End_Time)
    print("Job_List_Last_Time", Job_List_Last_Time)
    return Start_Time, End_Time, Job_List_Time, Job_List_Last_Time


##########################################################################################################################
##########################################################################################################################

# 三目标
#       1.makespan
#       2.机器总负载 机器的加工时间
#       3.关键机器负载 机器负载最大的
def Adaptability_For_MLoad_MCLoad(Idv, Map):
    _, _, Job_List_Time, Job_List_Last_Time = Decoder(Idv, Map)
    # 约束--优化目标
    Machine_Set = Idv.Machine_Chromosome  # MS
    Max_Finish_Time = 0  # 最大完成时间 -- min
    Machine_All_Load = 0.0  # 机器的总负载 -- min
    Critical_Machine_Load = 0.0  # 关键机器负载  关键机器也称为瓶颈机器 --min

    Machine_Using_Time = np.array(np.zeros(Map.Machine_Num, dtype=int))  # 记录每个机器的加工时间

    # 最大完成时间
    Max_Finish_Time = max(Job_List_Last_Time)
    # 机器总负载 关键机器负载
    for i in range(Map.Chromosome_Length):
        Machine_Using_Time[Machine_Set[i] - 1] += Job_List_Time[i]  # 计算每个机器的加工时间

    Machine_All_Load = sum(Machine_Using_Time.tolist())
    Critical_Machine_Load = max(Machine_Using_Time.tolist())
    print("模块：SetAdp\t方法：Adaptability_For_MLoad_MCLoad(Idv,Map)")
    return [Max_Finish_Time, Critical_Machine_Load, Machine_All_Load]
